package com.ecar.oauth.provider.config;

import com.ecar.oauth.provider.beans.OAuth2CustomRequestFactory;
import com.ecar.oauth.provider.beans.OAuthClientDetailsService;
import com.ecar.oauth.provider.beans.OAuthPasswordEncoder;
import com.ecar.oauth.provider.beans.OAuthRedisStore;
import com.ecar.oauth.provider.beans.OAuthTokenServices;
import com.ecar.oauth.provider.beans.OAuthUserApprovalHandler;
import com.ecar.oauth.provider.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * spring security oauth 配置
 * Created by 30 on 2016/12/22.
 * @noinspection UnnecessaryLocalVariable
 */
@Configuration
public class OAuthServerConfig {

	@Configuration
	@EnableAuthorizationServer
	protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

		protected final int EXPIRE = 7200;

		@Autowired
		@Qualifier("dataSource")
		private DataSource dataSource;

		@Autowired
		@Qualifier("authenticationManagerBean")
		private AuthenticationManager authenticationManager;

		@Resource
		private UserService userService;

		@Override
		public void configure(ClientDetailsServiceConfigurer client) throws Exception {
			client.jdbc(dataSource).passwordEncoder(passwordEncoder());
		}

		@Override
		public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
			security.allowFormAuthenticationForClients()
					.authenticationEntryPoint(authenticationEntryPoint())
					.accessDeniedHandler(accessDeniedHandler());
		}

		@Override
		public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
			endpoints.setClientDetailsService(clientDetailsService());
			endpoints.tokenStore(tokenStore())
					 .tokenServices(tokenServices())
					 .userApprovalHandler(userApprovalHandler())
					 .authenticationManager(authenticationManager)
					 .userDetailsService(userService)
					 .tokenGranter(tokenGranter())
					 .requestFactory(oAuth2RequestFactory())
					 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS);
//					 .exceptionTranslator(webResponseExceptionTranslator());
		}

		@Bean
		@Primary
		public ClientDetailsService clientDetailsService() {
			return new OAuthClientDetailsService(dataSource);
		}

		@Bean
		public TokenStore tokenStore() {
			return new OAuthRedisStore();
//			return new OAuthTokenStore(dataSource);
		}

		@Bean
		@Primary
		public AuthorizationServerTokenServices tokenServices() {
			OAuthTokenServices tokenServices = new OAuthTokenServices();
			tokenServices.setSupportRefreshToken(true);
			tokenServices.setTokenStore(tokenStore());
			tokenServices.setAccessTokenValiditySeconds(EXPIRE);
			tokenServices.setClientDetailsService(clientDetailsService());
			return tokenServices;
		}

		@Bean
		public ApprovalStore approvalStore() {
			TokenApprovalStore approvalStore = new TokenApprovalStore();
			approvalStore.setTokenStore(tokenStore());
			return approvalStore;
		}

		@Bean
		@Lazy
		@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
		public UserApprovalHandler userApprovalHandler() {
			OAuthUserApprovalHandler userApprovalHandler = new OAuthUserApprovalHandler();
			userApprovalHandler.setApprovalStore(approvalStore());
			userApprovalHandler.setRequestFactory(new OAuth2CustomRequestFactory(clientDetailsService()));
			userApprovalHandler.setClientDetailsService(clientDetailsService());
			return userApprovalHandler;
		}

		@Bean
		@Lazy
		@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
		public AccessDeniedHandler accessDeniedHandler() {
			OAuth2AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
//			accessDeniedHandler.setExceptionTranslator(webResponseExceptionTranslator());
			return accessDeniedHandler;
		}

		@Bean
		public TokenGranter tokenGranter() {
			org.springframework.security.oauth2.provider.ClientDetailsService clientDetails = clientDetailsService();
			AuthorizationServerTokenServices tokenServices = tokenServices();
			OAuth2RequestFactory oAuth2RequestFactory = oAuth2RequestFactory();

			RefreshTokenGranter refresh =
				new RefreshTokenGranter(tokenServices, clientDetails, oAuth2RequestFactory);
			ResourceOwnerPasswordTokenGranter password =
				new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
													  clientDetails, oAuth2RequestFactory);
			ClientCredentialsTokenGranter clientCredentials =
				new ClientCredentialsTokenGranter(tokenServices, clientDetails, oAuth2RequestFactory);
			clientCredentials.setAllowRefresh(true);

			List<TokenGranter> tokenGranters = new ArrayList<>();
			tokenGranters.add(refresh);
			tokenGranters.add(clientCredentials);
			tokenGranters.add(password);
			return new CompositeTokenGranter(tokenGranters);
		}

		@Bean
		public OAuth2RequestFactory oAuth2RequestFactory() {
			return new OAuth2CustomRequestFactory(clientDetailsService());
		}

		@Bean
		@Primary
		public PasswordEncoder passwordEncoder() {
			return new OAuthPasswordEncoder();
		}

//		/**
//		 * 自定义的异常处理类，返回json格式数据
//		 * @return
//		 */
//		@Bean
//		public WebResponseExceptionTranslator webResponseExceptionTranslator() {
//			return new JsonWebResponseExceptionTranslator();
//		}

		/**
		 * entry point 中注入自定义的异常处理
		 * @return
		 */
		@Bean
		public AuthenticationEntryPoint authenticationEntryPoint() {
			OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
//			authenticationEntryPoint.setExceptionTranslator(webResponseExceptionTranslator());
			return authenticationEntryPoint;
		}
	}
}
